
type HotkeyCallback = (event: KeyboardEvent) => void;
type HotkeyConfig = Record<string, boolean>;

type ModifierKey = 'Ctrl' | 'Shift' | 'Alt';

export default class hotkey {
    private hotkeys: Record<string, HotkeyCallback[]> = {};
    private hotkeysConfig: HotkeyConfig = {};
    private handleKeyDownEvent: (e: KeyboardEvent) => void;

    constructor() {
        this.handleKeyDownEvent = (e: KeyboardEvent) => this.handleKeyDown(e);
        document.addEventListener('keydown', this.handleKeyDownEvent, true);
    }

    /**
     * 热键销毁
     *
     * @memberof Hotkey
     */
    destroy() {
        this.hotkeys = {};
        document.removeEventListener('keydown', this.handleKeyDownEvent, true);
    }

    /**
     * 热键绑定
     *
     * @param {string} combo
     * @param {HotkeyCallback} callback
     * @param {{ macOnly?: boolean; windowsOnly?: boolean }} [options={}]
     * @param {boolean} [stop=false]
     * @return {*} 
     * @memberof Hotkey
     */
    addHotkey(
        combo: string,
        callback: HotkeyCallback,
        options: { macOnly?: boolean; windowsOnly?: boolean } = {},
        stop = false
    ) {
        if ((options.macOnly && !navigator.platform.startsWith('Mac')) || (options.windowsOnly && navigator.platform.startsWith('Mac'))) {
            return;
        }
        const _combo = combo.toLocaleLowerCase();
        if (!this.hotkeys[_combo]) {
            this.hotkeys[_combo] = [];
        }
        if (!this.hotkeysConfig[_combo]) {
            this.hotkeysConfig[_combo] = stop;
        }
        this.hotkeys[_combo].push(callback);
    }

    /**
     * 热键触发
     *
     * @private
     * @param {KeyboardEvent} event
     * @memberof Hotkey
     */
    private handleKeyDown(event: KeyboardEvent) {
        const { key, ctrlKey, altKey, shiftKey } = event;
        const modifierKeys: ModifierKey[] = [];
        // 使用 const 断言固定数组类型
        if (ctrlKey) modifierKeys.push('Ctrl');
        if (shiftKey) modifierKeys.push('Shift');
        if (altKey) modifierKeys.push('Alt');
        const combo = `${modifierKeys.join('+')}${modifierKeys.length ? '+' : ''}${key}`.toLocaleLowerCase();
        if (this.hotkeysConfig[combo]) {
            event.stopPropagation();
            event.preventDefault();
        }

        if (this.hotkeys[combo]) {
            this.hotkeys[combo].forEach(callback => {
                callback(event);
            });
        }
    }
}
